home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
aztcmtsk.arc
/
AZTASK.TSC
< prev
next >
Wrap
Text File
|
1986-03-24
|
10KB
|
359 lines
/* ========================================================== */
/* */
/* TASKING.C -- task and queue management in AZTEC C */
/* */
/* See TASKING.DOC for description and explanation. The */
/* interface declarations for this module, all that needs to */
/* be included in a client program, is in TASKING.H. The */
/* internal contents of the Queue, Event, and Task structures */
/* are visible only in this module with the code that uses */
/* them. */
/* Also included: a redefinition of the original AZTEC */
/* alloc(), with no improved function but with the dependecy */
/* on the hardware stack pointer removed. */
/* */
/* 3/1/86 coded */
/* 3/13/86 corrected method of stacking arguments */
/* */
/* Copyright (C) 1986 by David E. Cortesi, 415 Cambridge Ave */
/* Suite 18, Palo Alto, CA 94306. Permission to copy and */
/* redistribute without charge is granted provided only that */
/* this notice is retained. Distribution for a fee, whether */
/* alone or as part of another product and whether or not for */
/* profit, requires explicit permission from the author. */
/* ========================================================== */
#include "a:stdio.h" /* for deadlock msg to stderr */
struct Queue {
int g, /* get-index of oldest value */
p, /* put-index to next empty slot */
k, /* max elements and highest index */
w; /* nonzero if a task waits on this q */
unsigned *x; /* ->k+1 words of storage */
} ;
struct Event {
struct Queue EQ;/* an event is a one-item queue */
int buff[2]; /* with its item-buffer appended */
};
struct Task {
/* forward-chain to the next task in list, or 0 if end */
struct Task *nextask;
/* 0 = active, 1 = sleeping, -1 = dead, ->queue if blocked */
struct Queue *blocked;
/* number of words in the allocated stack */
int stksize;
/* saved CPU stack pointer (see #asm in defer() ) */
unsigned *stkptr;
/* address of allocated stack array */
unsigned *stack;
};
/*
this Task has no function but to anchor the chain (in a
sense, it represents CROOT). The word of 1 keeps it asleep
and it has no stack.
*/
static struct Task TaskHead = { 0,1,0,0,0 };
/*
CurTask points to the present executor, after whom a
newly-made task is inserted. Initializing to TaskHead
allows the first, main(), task, to be inserted.
*/
static struct Task *CurTask = &TaskHead;
/*
TasCount is incremented in maketask(), decremented in
endtask() and killtask(). When it drops to 0, the program
ends with a call to exit().
*/
static int TasCount = 0;
/* scratch words for scanning chain of tasks */
static struct Task *xt, *yt;
/* target of assembler save of stack pointer */
static unsigned SAVESP;
/*
when qget() finds an empty queue or qput() finds a full one,
they call qwait() to put the current task to sleep on the
indicated queue.
*/
/*void*/ qwait(adq) struct Queue *adq;
{
CurTask->blocked = adq;
adq->w = CurTask;
defer();
}
/*
when qget() takes from or qput adds to a queue on which a
task is shown as waiting, they call qwake() to wake up any
and all tasks so waiting.
*/
/*void*/ qwake(adq) register struct Queue *adq;
{
for(
xt = TaskHead.nextask;
xt;
xt = xt->nextask )
{
if (xt->blocked == adq)
xt->blocked = 0;
}
adq->w = 0;
}
/*
return the current task's Task address
*/
struct Task *myTask()
{ return(CurTask); }
/*
inquire into some task's status.
*/
int tactive(adt) register struct Task *adt;
{ return(adt->blocked == 0); }
int tinact(adt) register struct Task *adt;
{ return(adt->blocked); }
/*
To end a task we mark it with -1. The next time defer()
sees it, it drops it from the chain. The address of
endtask() is pushed on all stacks by maketask().
*/
/*void*/ endtask()
{
CurTask->blocked = -1; /* signal defer() to kill */
if (--TasCount > 0) defer();
else exit(0);
}
/*void*/ killtask(adt) register struct Task *adt;
{
adt->blocked = -1;
if (--TasCount > 0) return;
exit(0);
}
/*
to create a task, we make a task block and chain it
just after the current task.
*/
struct Task *maketask(fun,size,narg,arg0)
int (*fun)(), size, narg, arg0;
{
register struct Task *adt;
unsigned *arg;
/*
following is for AZTEC 8-bit, with nonstandard alloc, no
sizeof, and no type-checking
*/
adt = alloc(/*sizeof(struct Task)*/10);
/*
chain the new task in as immediate successor to the
currenttask, ahead of its siblings and after its parent.
*/
adt->nextask = CurTask->nextask;
CurTask->nextask = adt;
adt->blocked = 0;
adt->stksize = size;
++TasCount;
/*
set up the new task's stack by pushing onto it the given
arguments, then the address of endtask() in the position
of its caller's return address (so if it just returns,
it will end itself automatically), then its address so
that defer() will "return" to it.
*/
adt->stack = alloc(2*size);
adt->stkptr = adt->stack + size;
arg = (&arg0)+narg; /* 3/13/86 reproduce same stack.. */
for(;narg;--narg) /* 3/13/86 allow for narg==0 */
*--(adt->stkptr) = *--arg; /*3/13 ..sequence */
*--(adt->stkptr) = &endtask;
*--(adt->stkptr) = fun;
return(adt);
}
/*
suspend the active task and dispatch the next in sequence
*/
/*void*/ defer()
{
/*
get the present hardware SP value, which points to
defer()'s return address in the stack area of the
currently-active task, and save it.
*/
&SAVESP; /* sets HL->SAVESP */
#asm
xchg
lxi h,0
dad sp
xchg
mov m,e
inx h
mov m,d
#endasm
CurTask->stkptr = SAVESP;
/*
go hand-over-hand down the round-robin list looking
for a task that is ready to run.
*/
yt = CurTask;
for(;;)
{
xt = yt;
yt = xt->nextask;
if (yt == 0) /* *xt is last of chain, wrap */
yt = &TaskHead;
if (yt->blocked == 0) /* active task, stop */
break;
if (yt == CurTask) /* come all the way 'round */
{
fputs(
"\n\n*** IN DEADLOCK, ALL TASKS WAITING ***\n",
stderr);
exit(16);
}
if (yt->blocked == -1) /* ended task, kill */
{ /* remove from dispatch chain */
xt->nextask = yt->nextask;
/* here we would free the storage used by
yt->stack and yt->Task, if Aztec had free()*/
yt = xt;
}
}
/*
dispatch the active task we found by putting its stack
pointer value in the hardware SP and exiting.
*/
CurTask = yt;
SAVESP = CurTask->stkptr;
SAVESP; /* gets HL=SAVESP */
#asm
sphl
#endasm
}
/*
test a queue in various ways
*/
int qempty(adq) register struct Queue *adq;
{ return (adq->g == adq->p); }
int qnotmt(adq) register struct Queue *adq;
{ return (adq->g != adq->p); }
int qfull(adq) register struct Queue *adq;
{
register int np;
np = adq->p + 1;
if (np > adq->k) np = 0;
return(np == adq->g);
}
int qnotfull(adq) register struct Queue *adq;
{
register int np;
np = adq->p + 1;
if (np > adq->k) np = 0;
return(np != adq->g);
}
/*
make a queue to some size. the array of fifo data is
allocated separately to the Queue structure. one more
word must be allocated to this array than the max number
of items to be queued.
*/
struct Queue *qmake(size) int size;
{
register struct Queue *adq;
/* the following is for 8-bit AZTEC C, which lacks sizeof()
and a standard alloc, also lacks strict type-checks */
adq = alloc(/*sizeof(struct Queue)*/ 10);
adq->g = 0;
adq->p = 0;
adq->w = 0;
adq->k = size;
adq->x = alloc(size+size+2); /* allow k+1 unsigneds */
return(adq);
}
/*
make an Event, a 1-item queue, with the 2-word fifo buffer
contained in its structure.
*/
struct Event *evmake()
{
register struct Event *adq;
/* the following is for 8-bit AZTEC C, which lacks sizeof()
and a standard alloc, also lacks strict type-checks */
adq = alloc(/*sizeof(struct Event)*/ 14);
adq->EQ.g = 0;
adq->EQ.p = 0;
adq->EQ.w = 0;
adq->EQ.k = 2;
adq->EQ.x = &(adq->buff);
return(adq);
}
/*
get the oldest item from a queue, blocking the current task
if the queue is empty, and unblocking any tasks that might
have been blocked in a qput call.
*/
unsigned qget(adq) register struct Queue *adq;
{
unsigned val;
defer();
while (adq->g == adq->p) qwait(adq);
val = adq->x[adq->g++];
if (adq->g > adq->k) adq->g = 0;
if (adq->w) qwake(adq);
return(val);
}
/*
add an item to a queue, blocking the current task if the queue
is full, and unblocking any tasks that might have been blocked
in a qget call.
*/
/*void*/ qput(adq,val)
register struct Queue *adq;
unsigned val;
{
register int np;
for(;;)
{
np = adq->p + 1;
if (np > adq->k) np = 0;
if (adq->g != np) break;
qwait(adq);
}
adq->x[adq->p] = val;
adq->p = np;
if (adq->w) qwake(adq);
}
/*
The following version of alloc() replaces the original in
AZTEC C v 1.05, which used the hardware stack pointer as the
upper limit to storage. In a task, which has a small stack
area that was itself allocated from the heap, this is not the
thing to do. Here we assume that on first entry, we were
called from maketask() which was called from croot(), and so
the hardware SP is in fact the top of storage. We capture
this value for later use, and never test the SP thereafter.
*/
char *alloc(size) unsigned size;
{
static char *corebase = 0, *coremax = 0;
register char *cp;
if (corebase == 0) /* first call, from croot() */
{
corebase = settop(0); /* capture $MEMRY */
coremax = (char *)(&size) - 1024; /* ..and SP */
}
cp = corebase; /* presumed allocation point */
if (((corebase += size) < cp) /* wraparound */
|| (corebase > coremax) ) /* overflow */
{
corebase = cp; /* recover */
return(0); /* signal failure */
}
else return(cp);
}